# blib/xmpp.py
#
#

""" XMPP bot build on sleekxmpp. """

## blib imports

from blib import O, Bot, Event, error, stripped, attributes, subelements

## basic imports

import threading
import logging
import getpass
import types
import time
import _thread
import re

## Message class

class MESSAGE(Event): pass

## Presence class

class PRESENCE(Event): pass

## XMPP class

class XMPPBot(Bot):

    def __init__(self, *args, **kwargs):
        import sleekxmpp
        from sleekxmpp import clientxmpp
        Bot.__init__(self, *args, **kwargs)
        self.port = self.port or 5222
        self.xmpp =  clientxmpp.ClientXMPP(self.user, getpass.getpass())
        self.xmpp.add_event_handler("session_start", self.session_start)
        self.xmpp.add_event_handler("message", self.handle_message)
        self.xmpp.add_event_handler('disconnected', self.handle_disconnected)
        self.xmpp.add_event_handler('connected', self.handle_connected)
        self.xmpp.add_event_handler('presence_available', self.handle_presence)
        self.xmpp.add_event_handler('presence_dnd', self.handle_presence)
        self.xmpp.add_event_handler('presence_xa', self.handle_presence)
        self.xmpp.add_event_handler('presence_chat', self.handle_presence)
        self.xmpp.add_event_handler('presence_away', self.handle_presence)   
        self.xmpp.add_event_handler('presence_unavailable', self.handle_presence) 
        self.xmpp.add_event_handler('presence_subscribe', self.handle_presence)   
        self.xmpp.add_event_handler('presence_subscribed', self.handle_presence)  
        self.xmpp.add_event_handler('presence_unsubscribe', self.handle_presence) 
        self.xmpp.add_event_handler('presence_unsubscribed', self.handle_presence)
        self.xmpp.add_event_handler('groupchat_message', self.handle_message)
        self.xmpp.add_event_handler('groupchat_presence', self.handle_presence)
        self.xmpp.add_event_handler('groupchat_subject', self.handle_presence)
        self.xmpp.add_event_handler('failed_auth', self.handle_failedauth)
        self.xmpp.exception = self.exception
        self.xmpp.use_signals()
        if self.openfire:
            import ssl
            self.xmpp.ssl_version = ssl.PROTOCOL_SSLv3
        self._connected = threading.Event()

    def _raw(self, txt): self.xmpp.send_raw(txt)

    def connect(self): self.xmpp.connect((self.server, self.port), use_ssl=self.openfire)

    def session_start(self, event):
        logging.warn("session started")
        self.xmpp.send_presence()
        
    def exception(self, ex): logging.error(str(ex))

    def handle_failedauth(self, error, *args): logging.error(error) 

    def handle_failure(self, ex, *args, **kwargs): logging.error(str(ex))

    def handle_disconnected(self, *args, **kwargs): self._connected.clear() ; logging.error("server disconnected")

    def handle_connected(self, *args, **kwargs): self._connected.set() ; logging.warn("connected!")

    def run(self, connect=True, *args, **kwargs):
        if connect: self.connect()
        self.xmpp.process(block=True)

    def say(self, *args, **kwargs):  
        channel, txt = args
        e = Event(**kwargs)
        e.type = "chat"
        e.element = "message"
        e.to = channel
        e.txt = txt
        self.send(e)

    def send(self, event):
        try: xml = event.make_xmpp()
        except (AttributeError, TypeError): error() ; return
        logging.warn("> %s" % xml)
        self.xmpp.send_raw(xml)

    def handle_message(self, data, *args, **kwargs):
        if '<delay xmlns="urn:xmpp:delay"' in str(data): logging.debug("ignoring delayed message") ; return
        logging.warn("< %s" % data)
        m = MESSAGE(**data)
        if m.type == "error": logging.warn("error %s" % m.make_json()) ; return
        m.origin = stripped(str(m["from"]))
        m.channel = m.origin
        m.to = m.origin
        m.element = "message"
        try: m.txt = m["body"]
        except KeyError: pass
        m._target = self
        self.put(m)

    def handle_presence(self, data, *args, **kwargs):
        logging.info("< %s" % data)
        p = PRESENCE(**data)
        if "from" in p and p["from"]:
            p.origin = stripped(str(p["from"]))
            p.to = p.origin
        else: p.origin = "" ; p.to = ""
        p.element = "presence"
        if p.type == 'subscribe':
            pres = PRESENCE({'to': p["from"], 'type': 'subscribed'})
            self.send(pres)
            pres = PRSENCE({'to': p["from"], 'type': 'subscribe'})
            self.send(pres)
        p.no_dispatch = True
        p._target = self
        self.put(p)
